

package search.fitnessfunctions;


import java.util.*;
import primitives.cluster.*;

public class HierarchicalBunchMQ extends BunchTurboMQFitnessFunction{
	
	public double evaluate(ClusterHead ch){
		
		int depth = maxDepth(ch);

		ClusterHead toEval = extendLayers(ch);
			
		int level = 0;

		double total = 0;

		HashSet<IClusterLevel> current = new HashSet<IClusterLevel>();
		current.add(ch);

		while(!current.isEmpty()){
			HashSet<IClusterLevel> next = new HashSet<IClusterLevel>();
			ClusterHead temp = new ClusterHead(current, true);


			double fitness = super.evaluate(temp);

			//depth is decremented as we go down the three so the weight increases
			total += (fitness / (fitness+1))/depth;

			assert(depth != 0);
			for(IClusterLevel c: current){

				for(IClusterLevel d: ((ClusterNode)c).getChildren()){
					if(d.encapsulates()) next.add(d);
				}
			}
			depth--;
			current = next;
		}
		return total;
	}

	public static ClusterHead extendLayers(ClusterHead ch){
		ClusterHead copy= ch.deepCopy();

		int depth = maxDepth(copy)-1; //need to sub 1 because level will be 1 more than the max value when in the inner loop below.


		List<IClusterLevel> current = new ArrayList<IClusterLevel>();
		current.add(ch);


		int count = 0;
		while(!current.isEmpty()){
			List<IClusterLevel> next = new ArrayList<IClusterLevel>();

			for(IClusterLevel c: current){

				HashSet<IClusterLevel> children = new HashSet<IClusterLevel>();
				children.addAll(((ClusterNode)c).getChildren() );			
				for(IClusterLevel d: children){
//					count++;
//					System.out.println(depth + " " + count);
					if(d.encapsulates()) next.add(d);
					else{
						if(depth > 0){

							ClusterNode parent = ClusterUtils.addEmptyIgnoreID((ClusterNode)c, copy);
							ClusterUtils.move(d,(ClusterNode) c, parent, copy, false);
							next.add(parent);
						}
					}
				}
			}
			depth--;
			current = next;
		}


		return copy;
	}

	public static int maxDepth(ClusterHead ch){
		
		int level = 0;

		List<IClusterLevel> current = new ArrayList<IClusterLevel>();
		current.add(ch);

		while(!current.isEmpty()){
			List<IClusterLevel> next = new ArrayList<IClusterLevel>();

			for(IClusterLevel c: current){

				for(IClusterLevel d: ((ClusterNode)c).getChildren()){
					if(d.encapsulates()) next.add(d);
				}
			}
			level++;
			current = next;
		}
		return level;

	}
}